package com.lsh.oms.worker.service.task;

import com.alibaba.fastjson.JSONObject;
import com.dangdang.ddframe.job.api.JobExecutionMultipleShardingContext;
import com.dangdang.ddframe.job.plugin.job.type.simple.AbstractSimpleElasticJob;
import com.lsh.base.trace.id.filter.TraceIdSetter;
import com.lsh.oms.core.email.SendEmailService;
import com.lsh.oms.core.enums.EsOperateStatus;
import com.lsh.oms.core.enums.EsOperateType;
import com.lsh.oms.core.enums.EsOrderType;
import com.lsh.oms.core.enums.EsType;
import com.lsh.oms.core.model.es.EsOperate;
import com.lsh.oms.core.model.http.HttpMessage;
import com.lsh.oms.core.model.order.OrderHead;
import com.lsh.oms.core.service.es.EsOperateService;
import com.lsh.oms.core.service.order.OrderQueryService;
import com.lsh.oms.core.service.tool.ToolService;
import com.lsh.oms.core.utils.HttpUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

@Component
public class EsTaskDeal extends AbstractSimpleElasticJob {

	private static final Logger logger = LoggerFactory.getLogger(EsTaskDeal.class);

	@Autowired
	private EsOperateService esOperateService;

	@Autowired
	private SendEmailService sendEmailService;

	@Autowired
	private OrderQueryService orderQueryService;

	@Value("${elasticsearch.insertStatus}")
	private String elasticsearchInsertStatus;

	@Value("${elasticsearch.updateStatus}")
	private String elasticsearchUpdateStatus;

	@Value("${email.address}")
	private String emailAddress;

	@Value("${elasticsearch.number}")
	private Integer number;

//	@Value("${email.expireTime}")
//	private String emailExpireTime;

	private Map<Boolean, Set<String>> statusMap;

	private String[] emailAddressArray;

	private final Integer DEFAULT_STATUS = -1;

	@PostConstruct
	public void init() {
		statusMap = new HashMap<>();
		statusMap.put(true, ToolService.contentToSet(elasticsearchInsertStatus));
		statusMap.put(false, ToolService.contentToSet(elasticsearchUpdateStatus));

		emailAddressArray = emailAddress.split(",");
	}

	@Override
	public void process(JobExecutionMultipleShardingContext shardingContext) {
		TraceIdSetter.set();
		String uuid = UUID.randomUUID().toString();
		String msg = " [EsTaskDeal][" + uuid + "]";
		logger.info(msg + "[Es task deal start]");

		List<EsOperate> esOperateList = esOperateService.getEsOperateListByStatus(EsOperateStatus.FAIL.getIndex(), number);

		StringBuffer sbf = new StringBuffer();
		for(EsOperate esOperate : esOperateList) {
			try {
				boolean isSuccess = this.processOne(esOperate, msg);

				boolean isSend = false;
				if(!isSuccess) {
					if (esOperate.getNumber() + 1 >= number) {
						isSend = true;
					}
				}

				if(isSend) {
					sbf.append(getContent(esOperate, "")).append(System.getProperty("line.separator"));
				}
			} catch (Exception e) {
				logger.error(msg + "[Es task deal error][Error class:" + e.getClass() + ", error message:" + e.getMessage() + "]");
				logger.error(e.getMessage(), e);
				sbf.append(getContent(esOperate, e.getMessage())).append(System.getProperty("line.separator"));
			}
		}

		if(sbf.length() > 0) {
			this.sendEmail(sbf.toString(), msg);
		}
		logger.info(msg + "[Es task deal end]");
	}

	private boolean processOne(EsOperate esOperate, String msg) throws Exception {
		msg = msg + "[" + esOperate.getCode() + "]";
		logger.info(msg + "[process start]");
		logger.info(msg + "[order type:" + esOperate.getOrderType()
				+ ", status:" + esOperate.getStatus()
				+ ", url:" + esOperate.getUrl()
				+ ", data:" + esOperate.getData() + "]");

		//非订单忽略
		Integer orderType = esOperate.getOrderType();
		if (orderType == null || orderType.intValue() != EsOrderType.ORDER.getType()){
			logger.info(msg + "[process ignore...]");
			this.updateEsOperate(esOperate, true, msg);
			return true;
		}

		boolean isExecute = this.isExecute(esOperate, msg);
		if(!isExecute) {
			this.updateEsOperate(esOperate, true, msg);
			return true;
		}

		boolean isSuccess = true;

		String url = esOperate.getUrl();
		String data = esOperate.getData();
		Boolean isCreate = esOperate.getOperateType() == EsOperateType.INSERT.getIndex();
		EsType esType = isCreate ? EsType.CREATE : EsType.EDIT;
		try {
			logger.info(msg + "[send request start]");

			HttpMessage httpMessage = HttpUtils.sendPostRequest(url, data, HttpUtils.getDefaultHeadersWithContentType());

			if (!statusMap.get(isCreate).contains(httpMessage.getStatus())) {
				isSuccess = false;
			}

			logger.info(msg + "[send request end][" + esType.getDesc() + " index info][Info status:" + httpMessage.getStatus()
					+ ", info message:" + httpMessage.getMessage() + "]");
		} catch(IOException e) {
			isSuccess = false;
			logger.info(msg + "[send request end][" + esType.getDesc() + " index error][Error class:" + e.getClass()
					+ ", error message:" + e.getMessage() + "]");
			logger.error(e.getMessage(), e);
		}

		this.updateEsOperate(esOperate, isSuccess, msg);

		logger.info(msg + "[process end]");
		return isSuccess;
	}

	private boolean isExecute(EsOperate esOperate, String msg) {
		Boolean isCreate = esOperate.getOperateType() == EsOperateType.INSERT.getIndex();
		String data = esOperate.getData();

		boolean isExecute;
		if(isCreate) {
			isExecute = true;
		} else {
			Integer status = this.getStatus(data);

			if(status != null && !status.equals(DEFAULT_STATUS)) {
				Long orderCode = esOperate.getCode();
				OrderHead orderHead = orderQueryService.findByCode(orderCode, false, false);

				if(orderHead != null) {
					if(orderHead.getOrderStatus().compareTo(status) > 0) {
						isExecute = false;
					} else {
						isExecute = true;
					}
				} else {
					logger.error(msg + "[process error][Error message : order head not exist]");
					this.sendEmail("order head not exist, ID :" + esOperate.getId(), msg);
					isExecute = false;
				}

			} else {
				logger.error(msg + "[process error][Error message : order status not exist]");
//				this.sendEmail("order status not exist, ID :" + esOperate.getId(), msg);
				isExecute = false;
			}
		}
		return isExecute;
	}

	private void updateEsOperate(EsOperate esOperate, boolean isDefault, String msg) {
		if(isDefault) {
			esOperate.setStatus(EsOperateStatus.SUCCESS.getIndex());
		}

		Integer flag = esOperateService.updateStatus(esOperate);
		if(flag <= 0) {
			logger.error(msg + "[Update esOperate fail][ID:" + esOperate.getId() + "]");
		}
	}

	private static final String ES_DOC = "doc";

	private static final String ES_STATUS = "order_status";

	private Integer getStatus(String data) {
		JSONObject order = JSONObject.parseObject(data);
		if(order.containsKey(ES_DOC)) {
			order = JSONObject.parseObject(order.getString(ES_DOC));
		}

		if(!order.containsKey(ES_STATUS)) {
			return DEFAULT_STATUS;
		} else {
			return order.getInteger(ES_STATUS);
		}
	}

	private void sendEmail(String content, String msg) {
		msg = msg + "[send email]";

		logger.info(msg + "[start]");
		Boolean isSendEmail = this.sendEmailService.send(content, getTitle(), emailAddressArray);
		if(!isSendEmail) {
			logger.error(msg + "[error][send email fail]");
		}
		logger.error(msg + "[end]");
	}

	private static String getTitle() {
		String title = "ElasticSearch同步状态失败";
		return title;
	}

	private static String getContent(EsOperate esOperate, String reason) {
		StringBuffer sbf = new StringBuffer();
		sbf.append("ElasticSearch请求URL : ").append(esOperate.getUrl()).append(System.getProperty("line.separator"));
		sbf.append("ElasticSearch请求内容 : ").append(esOperate.getData()).append(System.getProperty("line.separator"));
		sbf.append("操作类型 : ")
				.append(esOperate.getOperateType() == EsOperateType.INSERT.getIndex() ? "create" : "edit")
				.append(System.getProperty("line.separator"));
		sbf.append("创建时间 : ").append(ToolService.getTimeByStamp(esOperate.getCreateTime())).append(System.getProperty("line.separator"));
		sbf.append("最近更新时间 : ").append(ToolService.getTimeByStamp(new Date().getTime())).append(System.getProperty("line.separator"));
		sbf.append("尝试次数 : ").append(esOperate.getNumber() + 1).append(System.getProperty("line.separator"));
		sbf.append("失败原因 : " + reason);
		return sbf.toString();
	}

}
